# Fichier: python_cheats/cheatsheets/analyse_de_données.txt
# Cheatsheet Analyse de Données Python - Guide Ultra-Détaillé pour Grands Débutants


[OK] CONCEPTS FONDAMENTAUX (EXPLICATIONS TRÈS DÉTAILLÉES)

# === QU'EST-CE QUE L'ANALYSE DE DONNÉES ? ===

# Imagine que tu as un fichier Excel avec 10,000 lignes de ventes
# Tu veux savoir:
# - Quel produit se vend le mieux?
# - Quel mois génère le plus de revenus?
# - Y a-t-il des tendances (patterns)?

# Solution classique (compliquée):
# 1. Ouvrir Excel, faire défiler 10,000 lignes
# 2. Créer manuellement des graphiques
# 3. Calculer des moyennes à la main
# 4. Copier-coller les résultats
# = BEAUCOUP DE TRAVAIL! Très lent et source d'erreurs.

# ANALYSE DE DONNÉES EN PYTHON = Automatiser tout ça!
# Tu ne fais que:
# 1. Charger les données dans Python
# 2. Écrire quelques lignes de code
# 3. BOOM! Des statistiques, graphiques, insights automatiques
# 4. Traiter des millions de lignes en quelques secondes

# L'analyse de données avec Python permet de:
# - Nettoyer les données (supprimer les doublons, corriger les erreurs)
# - Explorer les données (statistiques descriptives)
# - Visualiser les données (graphiques, charts)
# - Modéliser les données (prédictions, machine learning)
# - Automatiser des rapports


# === VOCABULAIRE ANALYSE DE DONNÉES (TRÈS IMPORTANT!) ===

# DATASET (Jeu de données)
# = Un ensemble de données structurées (comme une table Excel)
# = Contient des lignes (observations) et des colonnes (variables)
# Exemple: Un fichier CSV avec les ventes de ton entreprise
# Format typique:
# Date,Produit,Prix,Quantité
# 2024-01-15,Laptop,999,5
# 2024-01-16,Mouse,25,10

# DataFrame (Structure de données)
# = La structure principale pour manipuler des données tabulaires
# = Comme une feuille Excel mais en Python
# = Créé avec Pandas (la bibliothèque principale)
# Analogie: Si Excel est un classeur, DataFrame est une feuille

# Series (Colonne)
# = Une seule colonne d'un DataFrame
# = Comme une liste Python mais avec un index
# Exemple: La colonne "Prix" d'un DataFrame de ventes

# INDEX (Identifiant)
# = L'étiquette unique pour chaque ligne d'un DataFrame
# = Par défaut: 0, 1, 2, 3... mais peut être personnalisé
# Exemple: Utiliser les dates comme index au lieu de 0, 1, 2...

# NaN (Not a Number)
# = Valeur manquante dans les données
# = Équivalent de "cellule vide" dans Excel
# Exemple: Si tu as 100 clients mais que 5 n'ont pas renseigné leur email
# = 5 valeurs NaN dans la colonne "Email"

# AGRÉGATION (Regroupement)
# = Combiner plusieurs lignes en une seule statistique
# Exemples:
# - Somme des ventes par mois
# - Moyenne des notes par étudiant
# - Nombre de commandes par client

# VISUALISATION (Graphiques)
# = Représenter les données sous forme visuelle
# = Plus facile à comprendre qu'une table de chiffres
# Types: lignes, barres, camemberts, scatter plots, histogrammes

# NETTOYAGE DE DONNÉES (Data Cleaning)
# = Préparer les données avant analyse
# = Supprimer les doublons, corriger les erreurs, gérer les valeurs manquantes
# Exemple: "Paris" vs "paris" vs "PARIS" -> uniformiser en "Paris"


# === POURQUOI PYTHON POUR L'ANALYSE DE DONNÉES? ===

# 1. BIBLIOTHÈQUES PUISSANTES
# - Pandas: manipulation de données (comme Excel mais 1000x plus puissant)
# - NumPy: calculs numériques ultra-rapides
# - Matplotlib/Seaborn: graphiques professionnels
# - Scikit-learn: machine learning

# 2. RAPIDITÉ
# - Traite des millions de lignes en quelques secondes
# - Excel rame à partir de 100,000 lignes
# - Python gère facilement 10+ millions de lignes

# 3. REPRODUCTIBILITÉ
# - Ton code = documentation de ton analyse
# - Quelqu'un d'autre peut refaire ton analyse exactement
# - Avec Excel: impossible de savoir ce qui a été cliqué

# 4. AUTOMATISATION
# - Écris le code une fois, réutilise-le à l'infini
# - Génère des rapports automatiquement tous les jours
# - Exemple: rapport des ventes quotidiennes envoyé par email

# 5. GRATUIT ET OPEN SOURCE
# - Toutes les bibliothèques sont gratuites
# - Excel coûte cher en licence
# - Communauté massive pour t'aider


# === WORKFLOW COMPLET D'ANALYSE DE DONNÉES ===

# 1. COLLECTER LES DONNÉES
#    Sources: fichiers CSV, Excel, bases de données, APIs, web scraping
#    Exemple: Exporter les ventes depuis ton CRM

# 2. CHARGER LES DONNÉES
#    import pandas as pd
#    df = pd.read_csv('ventes.csv')
#    (Charge le fichier dans un DataFrame)

# 3. EXPLORER LES DONNÉES
#    df.head() -> Voir les premières lignes
#    df.info() -> Types de données, valeurs manquantes
#    df.describe() -> Statistiques descriptives

# 4. NETTOYER LES DONNÉES
#    - Supprimer les doublons: df.drop_duplicates()
#    - Gérer les NaN: df.fillna(0) ou df.dropna()
#    - Corriger les types: df['Date'] = pd.to_datetime(df['Date'])

# 5. ANALYSER LES DONNÉES
#    - Calculs: somme, moyenne, médiane, écart-type
#    - Grouper: ventes_par_mois = df.groupby('Mois')['Ventes'].sum()
#    - Filtrer: clients_actifs = df[df['Commandes'] > 5]

# 6. VISUALISER LES DONNÉES
#    import matplotlib.pyplot as plt
#    df.plot(kind='bar')
#    plt.show()

# 7. COMMUNIQUER LES RÉSULTATS
#    - Rapports PDF/HTML
#    - Dashboards interactifs (Plotly, Dash)
#    - Présentations PowerPoint


[OK] INSTALLATION SUPER DÉTAILLÉE

# === ÉTAPE 1: INSTALLER PYTHON ===

# Si tu n'as pas encore Python:
# 1. Va sur: https://www.python.org/downloads/
# 2. Télécharge la dernière version (3.11+ recommandé)
# 3. IMPORTANT: Coche "Add Python to PATH" lors de l'installation
# 4. Installe en cliquant sur "Install Now"

# Vérifier l'installation:
python --version
# Doit afficher: Python 3.11.X ou plus


# === ÉTAPE 2: CRÉER UN ENVIRONNEMENT VIRTUEL ===

# Pourquoi? Isoler tes projets et éviter les conflits de versions

# Linux/macOS:
python3 -m venv venv
source venv/bin/activate

# Windows:
python -m venv venv
venv\Scripts\activate

# Tu verras: (venv) $ ou (venv) C:\...
# = L'environnement virtuel est ACTIVÉ


# === ÉTAPE 3: INSTALLER LES BIBLIOTHÈQUES ESSENTIELLES ===

# PANDAS = Manipulation de données
pip install pandas

# NUMPY = Calculs numériques
pip install numpy

# MATPLOTLIB = Graphiques de base
pip install matplotlib

# SEABORN = Graphiques statistiques avancés
pip install seaborn

# SCIPY = Statistiques avancées
pip install scipy

# OPENPYXL = Lire/écrire des fichiers Excel
pip install openpyxl

# JUPYTERLAB = Interface interactive (optionnel mais recommandé)
pip install jupyterlab

# OU installer tout d'un coup:
pip install pandas numpy matplotlib seaborn scipy openpyxl jupyterlab

# Vérifier l'installation:
pip list
# Doit afficher toutes les bibliothèques installées


# === ÉTAPE 4: LANCER JUPYTER LAB (RECOMMANDÉ POUR DÉBUTER) ===

# Pourquoi Jupyter Lab?
# - Interface web interactive
# - Exécute du code cellule par cellule
# - Voir les résultats immédiatement
# - Idéal pour l'apprentissage et l'exploration

# Lancer Jupyter Lab:
jupyter lab

# Un navigateur s'ouvre automatiquement
# Tu vois une interface avec un explorateur de fichiers
# Clique sur "Notebook" -> "Python 3" pour créer un nouveau notebook

# Alternative: Jupyter Notebook classique
jupyter notebook


# === ÉTAPE 5: CRÉER TON PREMIER NOTEBOOK ===

# 1. Dans Jupyter Lab, clique "File" -> "New" -> "Notebook"
# 2. Nomme-le "mon_premier_notebook.ipynb"
# 3. Dans la première cellule, tape:
import pandas as pd
print("Pandas est installé!")

# 4. Appuie sur Shift+Enter pour exécuter
# 5. Tu vois: "Pandas est installé!"
# BRAVO! Tout est prêt!


[OK] PANDAS: LA BIBLIOTHÈQUE FONDAMENTALE

# === INTRODUCTION À PANDAS ===

# Pandas = Bibliothèque Python pour manipuler des données tabulaires
# = L'équivalent d'Excel mais en code
# = Peut traiter des millions de lignes rapidement

# Importer Pandas:
import pandas as pd

# Convention: toujours importer comme "pd" (raccourci standard)


# === CRÉER UN DATAFRAME (PLUSIEURS MÉTHODES) ===

# === MÉTHODE 1: À partir d'un dictionnaire ===

# Pourquoi? Pour créer rapidement des données de test

data = {
    'Nom': ['Alice', 'Bob', 'Charlie', 'David'],
    'Âge': [25, 30, 35, 28],
    'Ville': ['Paris', 'Lyon', 'Marseille', 'Toulouse'],
    'Salaire': [3000, 3500, 4000, 3200]
}

df = pd.DataFrame(data)

# Explications:
# - data = dictionnaire Python
# - Clés du dictionnaire = noms des colonnes
# - Valeurs du dictionnaire = listes des données
# - pd.DataFrame(data) = convertit en DataFrame

# Afficher le DataFrame:
print(df)

# Résultat:
#        Nom  Âge       Ville  Salaire
# 0    Alice   25       Paris     3000
# 1      Bob   30        Lyon     3500
# 2  Charlie   35   Marseille     4000
# 3    David   28    Toulouse     3200


# === MÉTHODE 2: À partir d'un fichier CSV ===

# Pourquoi? Les données viennent souvent de fichiers CSV/Excel

# Créer un fichier CSV d'exemple:
# Crée un fichier "ventes.csv" avec ce contenu:
# Date,Produit,Prix,Quantité
# 2024-01-15,Laptop,999,5
# 2024-01-16,Mouse,25,10
# 2024-01-17,Keyboard,75,8

# Charger le CSV:
df = pd.read_csv('ventes.csv')

# Explications:
# - pd.read_csv() = fonction pour lire un CSV
# - 'ventes.csv' = chemin vers le fichier
# - Retourne un DataFrame avec les données du CSV

print(df)

# Résultat:
#          Date  Produit  Prix  Quantité
# 0  2024-01-15   Laptop   999         5
# 1  2024-01-16    Mouse    25        10
# 2  2024-01-17 Keyboard    75         8


# === MÉTHODE 3: À partir d'un fichier Excel ===

# Pourquoi? Beaucoup d'entreprises utilisent Excel

# Charger un fichier Excel:
df = pd.read_excel('ventes.xlsx', sheet_name='Feuil1')

# Explications:
# - pd.read_excel() = fonction pour lire Excel
# - sheet_name='Feuil1' = nom de la feuille à lire
# - Par défaut: lit la première feuille

# Lire plusieurs feuilles:
df_dict = pd.read_excel('ventes.xlsx', sheet_name=None)
# Retourne un dictionnaire de DataFrames (une par feuille)


# === MÉTHODE 4: À partir d'une base de données ===

# Pourquoi? Les données de production sont souvent dans des BDD

import sqlite3

# Se connecter à une base SQLite:
conn = sqlite3.connect('ma_base.db')

# Lire une table SQL:
df = pd.read_sql_query("SELECT * FROM ventes", conn)

# Fermer la connexion:
conn.close()

# Explications:
# - sqlite3.connect() = connexion à la BDD
# - pd.read_sql_query() = exécute une requête SQL et retourne un DataFrame
# - Fonctionne aussi avec PostgreSQL, MySQL, etc. (utiliser sqlalchemy)


# === MÉTHODE 5: À partir d'une API (JSON) ===

# Pourquoi? Récupérer des données depuis Internet

import requests

# Récupérer des données depuis une API:
response = requests.get('https://api.example.com/ventes')
data = response.json()

# Convertir en DataFrame:
df = pd.DataFrame(data)

# Explications:
# - requests.get() = faire une requête HTTP GET
# - response.json() = parser la réponse JSON
# - pd.DataFrame() = convertir en DataFrame


# === EXPLORER UN DATAFRAME ===

# === AFFICHER LES PREMIÈRES LIGNES ===

# Pourquoi? Pour avoir un aperçu rapide des données

df.head()
# Affiche les 5 premières lignes (par défaut)

df.head(10)
# Affiche les 10 premières lignes

# Résultat (exemple):
#        Nom  Âge       Ville  Salaire
# 0    Alice   25       Paris     3000
# 1      Bob   30        Lyon     3500
# 2  Charlie   35   Marseille     4000
# 3    David   28    Toulouse     3200
# 4      Eve   22   Bordeaux     2800


# === AFFICHER LES DERNIÈRES LIGNES ===

df.tail()
# Affiche les 5 dernières lignes

df.tail(3)
# Affiche les 3 dernières lignes


# === AFFICHER DES LIGNES ALÉATOIRES ===

# Pourquoi? Pour avoir un échantillon représentatif

df.sample(5)
# Affiche 5 lignes aléatoires

df.sample(n=10, random_state=42)
# random_state=42 = fixer le hasard (reproductible)


# === INFORMATIONS SUR LE DATAFRAME ===

df.info()

# Affiche:
# - Nombre de lignes et colonnes
# - Types de données de chaque colonne
# - Nombre de valeurs non-nulles
# - Utilisation mémoire

# Résultat (exemple):
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 4 entries, 0 to 3
# Data columns (total 4 columns):
#  #   Column   Non-Null Count  Dtype 
# ---  ------   --------------  ----- 
#  0   Nom      4 non-null      object
#  1   Âge      4 non-null      int64 
#  2   Ville    4 non-null      object
#  3   Salaire  4 non-null      int64 
# dtypes: int64(2), object(2)
# memory usage: 256.0+ bytes


# === DIMENSIONS DU DATAFRAME ===

df.shape
# Retourne: (nombre_lignes, nombre_colonnes)

# Exemple:
# (4, 4) = 4 lignes, 4 colonnes

# Accéder séparément:
nb_lignes = df.shape[0]
nb_colonnes = df.shape[1]

print(f"Le DataFrame a {nb_lignes} lignes et {nb_colonnes} colonnes")


# === NOMS DES COLONNES ===

df.columns

# Retourne: Index(['Nom', 'Âge', 'Ville', 'Salaire'], dtype='object')

# Convertir en liste:
liste_colonnes = df.columns.tolist()
# ['Nom', 'Âge', 'Ville', 'Salaire']


# === TYPES DE DONNÉES ===

df.dtypes

# Affiche le type de chaque colonne:
# Nom         object  (texte)
# Âge          int64  (entier)
# Ville       object  (texte)
# Salaire      int64  (entier)


# === STATISTIQUES DESCRIPTIVES ===

df.describe()

# Affiche (pour colonnes numériques seulement):
# - count: nombre de valeurs
# - mean: moyenne
# - std: écart-type
# - min: valeur minimale
# - 25%, 50%, 75%: quartiles
# - max: valeur maximale

# Résultat (exemple):
#              Âge     Salaire
# count   4.000000     4.00000
# mean   29.500000  3425.00000
# std     4.358899   447.21360
# min    25.000000  3000.00000
# 25%    27.250000  3125.00000
# 50%    29.000000  3350.00000
# 75%    31.250000  3650.00000
# max    35.000000  4000.00000

# Inclure les colonnes texte:
df.describe(include='all')


# === COMPTER LES VALEURS UNIQUES ===

df['Ville'].nunique()
# Retourne: 4 (il y a 4 villes différentes)

df['Ville'].unique()
# Retourne: array(['Paris', 'Lyon', 'Marseille', 'Toulouse'], dtype=object)

# Compter les occurrences:
df['Ville'].value_counts()

# Résultat:
# Paris        1
# Lyon         1
# Marseille    1
# Toulouse     1
# Name: Ville, dtype: int64


# === VÉRIFIER LES VALEURS MANQUANTES ===

df.isnull()
# Retourne un DataFrame de True/False
# True = valeur manquante (NaN)

df.isnull().sum()
# Compte les NaN par colonne

# Résultat (exemple):
# Nom        0
# Âge        0
# Ville      0
# Salaire    1
# dtype: int64
# (1 salaire manquant)

# Pourcentage de valeurs manquantes:
(df.isnull().sum() / len(df)) * 100


[OK] SÉLECTION ET FILTRAGE DE DONNÉES

# === SÉLECTIONNER UNE COLONNE ===

# MÉTHODE 1: Avec crochets (recommandé)
ages = df['Âge']
# Retourne une Series

# MÉTHODE 2: Avec point (seulement si pas d'espace dans le nom)
ages = df.Âge
# Moins recommandé (peut causer des problèmes)

# Afficher:
print(ages)

# Résultat:
# 0    25
# 1    30
# 2    35
# 3    28
# Name: Âge, dtype: int64


# === SÉLECTIONNER PLUSIEURS COLONNES ===

# Pourquoi? Pour ne garder que les colonnes qui t'intéressent

colonnes_interet = df[['Nom', 'Salaire']]

# ATTENTION: Double crochets [[...]]!
# Retourne un DataFrame (pas une Series)

print(colonnes_interet)

# Résultat:
#        Nom  Salaire
# 0    Alice     3000
# 1      Bob     3500
# 2  Charlie     4000
# 3    David     3200


# === SÉLECTIONNER DES LIGNES PAR INDEX ===

# === MÉTHODE 1: iloc (par position) ===

# Pourquoi? Pour sélectionner par numéro de ligne

# Première ligne:
df.iloc[0]

# Lignes 0 à 2 (3 premières):
df.iloc[0:3]

# Lignes 1 et 3:
df.iloc[[1, 3]]

# Dernière ligne:
df.iloc[-1]

# Toutes les lignes, colonne 1:
df.iloc[:, 1]


# === MÉTHODE 2: loc (par label) ===

# Pourquoi? Pour sélectionner par nom d'index ou de colonne

# Ligne avec index 0:
df.loc[0]

# Lignes 0 à 2, colonnes 'Nom' et 'Âge':
df.loc[0:2, ['Nom', 'Âge']]

# Toutes les lignes, colonne 'Salaire':
df.loc[:, 'Salaire']


# === FILTRAGE AVEC CONDITIONS ===

# === FILTRER SELON UNE CONDITION ===

# Pourquoi? Pour extraire un sous-ensemble de données

# Personnes de plus de 30 ans:
df_plus_30 = df[df['Âge'] > 30]

# Explications:
# - df['Âge'] > 30 = crée une Series de True/False
# - df[...] = garde seulement les lignes True

print(df_plus_30)

# Résultat:
#        Nom  Âge       Ville  Salaire
# 2  Charlie   35   Marseille     4000


# === FILTRER AVEC PLUSIEURS CONDITIONS ===

# Personnes de plus de 25 ans ET salaire > 3000:
df_filtre = df[(df['Âge'] > 25) & (df['Salaire'] > 3000)]

# ATTENTION: Utiliser & (ET) et | (OU), pas "and" et "or"
# ET: &
# OU: |

# Exemple avec OU:
df_filtre = df[(df['Âge'] > 30) | (df['Salaire'] > 3500)]

# Explications:
# - Chaque condition entre parenthèses
# - & = ET logique
# - | = OU logique


# === FILTRER AVEC isin() ===

# Pourquoi? Pour vérifier si une valeur est dans une liste

# Personnes habitant Paris ou Lyon:
villes = ['Paris', 'Lyon']
df_filtre = df[df['Ville'].isin(villes)]

print(df_filtre)

# Résultat:
#      Nom  Âge  Ville  Salaire
# 0  Alice   25  Paris     3000
# 1    Bob   30   Lyon     3500


# === FILTRER AVEC str (chaînes de caractères) ===

# Pourquoi? Pour rechercher des patterns dans du texte

# Noms commençant par 'A':
df_filtre = df[df['Nom'].str.startswith('A')]

# Noms contenant 'ar':
df_filtre = df[df['Nom'].str.contains('ar')]

# Villes en majuscules:
df_filtre = df[df['Ville'].str.isupper()]

# Noms de longueur > 5:
df_filtre = df[df['Nom'].str.len() > 5]


# === FILTRER LES VALEURS MANQUANTES ===

# Garder seulement les lignes SANS NaN dans la colonne 'Salaire':
df_sans_nan = df[df['Salaire'].notna()]

# Garder seulement les lignes AVEC NaN:
df_avec_nan = df[df['Salaire'].isna()]


# === FILTRER AVEC query() (SYNTAXE SIMPLIFIÉE) ===

# Pourquoi? Syntaxe plus lisible pour des filtres complexes

# Personnes de plus de 30 ans:
df_filtre = df.query('Âge > 30')

# Plusieurs conditions:
df_filtre = df.query('Âge > 25 and Salaire > 3000')

# Utiliser des variables externes:
age_min = 28
df_filtre = df.query('Âge > @age_min')


[OK] MANIPULATION DE DONNÉES

# === AJOUTER UNE COLONNE ===

# === MÉTHODE 1: Calcul simple ===

# Ajouter une colonne "Bonus" (10% du salaire):
df['Bonus'] = df['Salaire'] * 0.10

print(df)

# Résultat:
#        Nom  Âge       Ville  Salaire  Bonus
# 0    Alice   25       Paris     3000  300.0
# 1      Bob   30        Lyon     3500  350.0
# 2  Charlie   35   Marseille     4000  400.0
# 3    David   28    Toulouse     3200  320.0


# === MÉTHODE 2: Calcul conditionnel avec np.where ===

import numpy as np

# Ajouter une colonne "Catégorie" selon l'âge:
df['Catégorie'] = np.where(df['Âge'] >= 30, 'Senior', 'Junior')

# Explications:
# - np.where(condition, valeur_si_vrai, valeur_si_faux)
# - Si Âge >= 30: 'Senior', sinon 'Junior'

print(df)

# Résultat:
#        Nom  Âge       Ville  Salaire Catégorie
# 0    Alice   25       Paris     3000    Junior
# 1      Bob   30        Lyon     3500    Senior
# 2  Charlie   35   Marseille     4000    Senior
# 3    David   28    Toulouse     3200    Junior


# === MÉTHODE 3: Avec apply() (fonctions personnalisées) ===

# Créer une fonction personnalisée:
def categoriser_salaire(salaire):
    if salaire < 3200:
        return 'Bas'
    elif salaire < 3800:
        return 'Moyen'
    else:
        return 'Élevé'

# Appliquer la fonction:
df['Niveau_Salaire'] = df['Salaire'].apply(categoriser_salaire)

# Explications:
# - apply() applique une fonction à chaque valeur de la colonne
# - Prend chaque salaire, passe dans la fonction, retourne le résultat


# === MÉTHODE 4: Avec lambda (fonctions anonymes) ===

# Lambda = fonction courte d'une ligne

# Calculer l'année de naissance (en 2024):
df['Année_Naissance'] = df['Âge'].apply(lambda x: 2024 - x)

# Explications:
# - lambda x: 2024 - x = fonction qui prend x et retourne 2024 - x
# - Équivalent à: def calcul(x): return 2024 - x


# === SUPPRIMER UNE COLONNE ===

# Supprimer la colonne 'Bonus':
df = df.drop('Bonus', axis=1)

# ATTENTION: axis=1 signifie "colonne"
# axis=0 signifie "ligne"

# Alternative (plus moderne):
df = df.drop(columns=['Bonus'])

# Supprimer plusieurs colonnes:
df = df.drop(columns=['Bonus', 'Catégorie'])

# IMPORTANT: drop() ne modifie PAS le DataFrame original
# Il retourne un nouveau DataFrame
# Pour modifier l'original:
df.drop('Bonus', axis=1, inplace=True)


# === RENOMMER DES COLONNES ===

# Renommer une colonne:
df = df.rename(columns={'Âge': 'Age'})

# Renommer plusieurs colonnes:
df = df.rename(columns={'Âge': 'Age', 'Salaire': 'Revenu'})

# Renommer toutes les colonnes (liste):
df.columns = ['Nom', 'Age', 'Ville', 'Revenu']


# === MODIFIER LES TYPES DE DONNÉES ===

# Pourquoi? Les types incorrects causent des erreurs

# Convertir en entier:
df['Age'] = df['Age'].astype(int)

# Convertir en float:
df['Salaire'] = df['Salaire'].astype(float)

# Convertir en string (texte):
df['Age'] = df['Age'].astype(str)

# Convertir en datetime (dates):
df['Date'] = pd.to_datetime(df['Date'])

# Format personnalisé:
df['Date'] = pd.to_datetime(df['Date'], format='%d/%m/%Y')


# === TRIER LES DONNÉES ===

# Trier par une colonne (ordre croissant):
df_trie = df.sort_values('Âge')

# Trier par ordre décroissant:
df_trie = df.sort_values('Âge', ascending=False)

# Trier par plusieurs colonnes:
df_trie = df.sort_values(['Ville', 'Âge'])

# Modifier l'original:
df.sort_values('Âge', inplace=True)


# === GÉRER LES DOUBLONS ===

# Vérifier s'il y a des doublons:
df.duplicated().sum()
# Retourne: nombre de lignes en double

# Afficher les lignes en double:
df[df.duplicated()]

# Supprimer les doublons:
df = df.drop_duplicates()

# Garder seulement la première occurrence:
df = df.drop_duplicates(keep='first')

# Garder la dernière:
df = df.drop_duplicates(keep='last')

# Basé sur certaines colonnes seulement:
df = df.drop_duplicates(subset=['Nom', 'Ville'])


# === GÉRER LES VALEURS MANQUANTES (NaN) ===


# === DÉTECTER LES NaN ===

df.isnull()
# DataFrame de True/False

df.isnull().sum()
# Nombre de NaN par colonne

# Visualiser:
import seaborn as sns
import matplotlib.pyplot as plt

sns.heatmap(df.isnull(), cbar=False)
plt.show()
# Carte de chaleur des NaN


# === SUPPRIMER LES NaN ===

# Supprimer toutes les lignes avec au moins un NaN:
df_clean = df.dropna()

# Supprimer seulement si TOUTES les valeurs sont NaN:
df_clean = df.dropna(how='all')

# Supprimer seulement dans certaines colonnes:
df_clean = df.dropna(subset=['Salaire', 'Âge'])


# === REMPLACER LES NaN ===

# Remplacer par 0:
df_filled = df.fillna(0)

# Remplacer par la moyenne:
df['Salaire'] = df['Salaire'].fillna(df['Salaire'].mean())

# Remplacer par la médiane:
df['Salaire'] = df['Salaire'].fillna(df['Salaire'].median())

# Remplacer par la valeur la plus fréquente:
df['Ville'] = df['Ville'].fillna(df['Ville'].mode()[0])

# Remplacer avec forward fill (propagation avant):
df_filled = df.fillna(method='ffill')
# Utilise la valeur précédente

# Remplacer avec backward fill (propagation arrière):
df_filled = df.fillna(method='bfill')
# Utilise la valeur suivante


# === RÉINITIALISER L'INDEX ===

# Pourquoi? Après filtrage, l'index peut être 0, 5, 7, 9...
# On veut 0, 1, 2, 3...

df = df.reset_index(drop=True)

# drop=True = ne pas garder l'ancien index comme colonne


# === DÉFINIR UNE COLONNE COMME INDEX ===

# Utiliser la colonne 'Date' comme index:
df = df.set_index('Date')

# Revenir à l'index numérique:
df = df.reset_index()


[OK] AGRÉGATION ET GROUPEMENT

# === STATISTIQUES SIMPLES ===

# Moyenne:
df['Salaire'].mean()

# Médiane (valeur du milieu):
df['Salaire'].median()

# Mode (valeur la plus fréquente):
df['Ville'].mode()[0]

# Somme:
df['Salaire'].sum()

# Min / Max:
df['Salaire'].min()
df['Salaire'].max()

# Écart-type (dispersion):
df['Salaire'].std()

# Variance:
df['Salaire'].var()

# Quantiles:
df['Salaire'].quantile(0.25)  # 1er quartile
df['Salaire'].quantile(0.50)  # Médiane
df['Salaire'].quantile(0.75)  # 3e quartile


# === GROUPBY: AGRÉGATION PAR GROUPE ===

# === CONCEPT DE GROUPBY ===

# Imagine que tu as des ventes par ville:
# Paris: 3000, 2500, 3200
# Lyon: 4000, 3800
# Marseille: 5000

# Tu veux la somme des ventes PAR ville
# = Grouper par ville, puis sommer

# Exemple de données:
data = {
    'Ville': ['Paris', 'Paris', 'Lyon', 'Lyon', 'Marseille'],
    'Ventes': [3000, 2500, 4000, 3800, 5000]
}
df = pd.DataFrame(data)


# === GROUPBY SIMPLE ===

# Somme des ventes par ville:
ventes_par_ville = df.groupby('Ville')['Ventes'].sum()

# Explications:
# 1. df.groupby('Ville') = regroupe les lignes par ville
# 2. ['Ventes'] = sélectionne la colonne à agréger
# 3. .sum() = fonction d'agrégation

print(ventes_par_ville)

# Résultat:
# Ville
# Lyon         7800
# Marseille    5000
# Paris        5500
# Name: Ventes, dtype: int64


# === GROUPBY AVEC PLUSIEURS FONCTIONS ===

# Statistiques multiples:
stats = df.groupby('Ville')['Ventes'].agg(['sum', 'mean', 'count'])

# .agg() = agrégation avec plusieurs fonctions

print(stats)

# Résultat:
#             sum    mean  count
# Ville                         
# Lyon       7800  3900.0      2
# Marseille  5000  5000.0      1
# Paris      5500  2750.0      2


# === GROUPBY SUR PLUSIEURS COLONNES ===

# Exemple avec Ville ET Mois:
data = {
    'Ville': ['Paris', 'Paris', 'Lyon', 'Lyon', 'Paris'],
    'Mois': ['Jan', 'Fev', 'Jan', 'Fev', 'Jan'],
    'Ventes': [3000, 2500, 4000, 3800, 3200]
}
df = pd.DataFrame(data)

# Grouper par Ville ET Mois:
stats = df.groupby(['Ville', 'Mois'])['Ventes'].sum()

print(stats)

# Résultat:
# Ville      Mois
# Lyon       Fev     3800
#            Jan     4000
# Paris      Fev     2500
#            Jan     6200  (3000 + 3200)
# Name: Ventes, dtype: int64


# === GROUPBY AVEC FONCTIONS PERSONNALISÉES ===

# Créer une fonction d'agrégation:
def ecart(x):
    return x.max() - x.min()

# Appliquer:
df.groupby('Ville')['Ventes'].agg(ecart)


# === PIVOT TABLE (TABLEAU CROISÉ) ===

# Pourquoi? Pour réorganiser les données en tableau croisé

# Exemple: Ventes par Ville (lignes) et Mois (colonnes)
pivot = df.pivot_table(
    values='Ventes',
    index='Ville',
    columns='Mois',
    aggfunc='sum'
)

print(pivot)

# Résultat:
# Mois       Fev   Jan
# Ville              
# Lyon      3800  4000
# Paris     2500  6200

# Explications:
# - values='Ventes' = colonne à agréger
# - index='Ville' = lignes du tableau
# - columns='Mois' = colonnes du tableau
# - aggfunc='sum' = fonction d'agrégation


# === CROSSTAB (TABLEAU DE FRÉQUENCE) ===

# Pourquoi? Pour compter les occurrences

# Nombre de ventes par Ville et Mois:
ct = pd.crosstab(df['Ville'], df['Mois'])

print(ct)

# Résultat:
# Mois       Fev  Jan
# Ville             
# Lyon         1    1
# Paris        1    2


[OK] FUSION ET JOINTURE DE DATAFRAMES

# === CONCATÉNATION (EMPILER DES DATAFRAMES) ===

# Pourquoi? Pour combiner des DataFrames ayant les mêmes colonnes

df1 = pd.DataFrame({
    'Nom': ['Alice', 'Bob'],
    'Âge': [25, 30]
})

df2 = pd.DataFrame({
    'Nom': ['Charlie', 'David'],
    'Âge': [35, 28]
})

# Concaténer verticalement (empiler):
df_concat = pd.concat([df1, df2], ignore_index=True)

# ignore_index=True = réinitialiser l'index à 0, 1, 2, 3...

print(df_concat)

# Résultat:
#        Nom  Âge
# 0    Alice   25
# 1      Bob   30
# 2  Charlie   35
# 3    David   28


# Concaténer horizontalement (côte à côte):
df_concat = pd.concat([df1, df2], axis=1)

# axis=1 = concaténer par colonnes


# === MERGE (JOINTURE COMME EN SQL) ===

# === CONCEPT DE MERGE ===

# Imagine deux tables:
# Clients: ID, Nom, Ville
# Commandes: ID_Commande, ID_Client, Montant

# Tu veux combiner pour avoir:
# ID_Commande, ID_Client, Nom, Ville, Montant

# = JOINTURE sur ID_Client


# === EXEMPLE CONCRET ===

# DataFrame Clients:
clients = pd.DataFrame({
    'ID_Client': [1, 2, 3, 4],
    'Nom': ['Alice', 'Bob', 'Charlie', 'David'],
    'Ville': ['Paris', 'Lyon', 'Marseille', 'Toulouse']
})

# DataFrame Commandes:
commandes = pd.DataFrame({
    'ID_Commande': [101, 102, 103],
    'ID_Client': [1, 2, 1],
    'Montant': [500, 300, 700]
})


# === INNER JOIN (INTERSECTION) ===

# Garder seulement les clients QUI ONT des commandes:
df_merge = pd.merge(clients, commandes, on='ID_Client', how='inner')

print(df_merge)

# Résultat:
#    ID_Client     Nom  Ville  ID_Commande  Montant
# 0          1   Alice  Paris          101      500
# 1          1   Alice  Paris          103      700
# 2          2     Bob   Lyon          102      300

# Explications:
# - on='ID_Client' = colonne commune pour joindre
# - how='inner' = seulement les lignes qui matchent


# === LEFT JOIN (GARDER TOUT À GAUCHE) ===

# Garder TOUS les clients, même sans commandes:
df_merge = pd.merge(clients, commandes, on='ID_Client', how='left')

print(df_merge)

# Résultat:
#    ID_Client     Nom       Ville  ID_Commande  Montant
# 0          1   Alice       Paris        101.0    500.0
# 1          1   Alice       Paris        103.0    700.0
# 2          2     Bob        Lyon        102.0    300.0
# 3          3 Charlie   Marseille          NaN      NaN
# 4          4   David    Toulouse          NaN      NaN

# Charlie et David n'ont pas de commandes -> NaN


# === RIGHT JOIN (GARDER TOUT À DROITE) ===

df_merge = pd.merge(clients, commandes, on='ID_Client', how='right')

# Garder TOUTES les commandes, même si client inconnu


# === OUTER JOIN (TOUT GARDER) ===

df_merge = pd.merge(clients, commandes, on='ID_Client', how='outer')

# Garde tout: clients sans commandes ET commandes sans clients


# === MERGE SUR PLUSIEURS COLONNES ===

df_merge = pd.merge(
    df1, df2,
    on=['Colonne1', 'Colonne2'],
    how='inner'
)


# === MERGE AVEC NOMS DE COLONNES DIFFÉRENTS ===

# Si la colonne s'appelle 'ID_Client' dans df1 et 'Client_ID' dans df2:
df_merge = pd.merge(
    clients, commandes,
    left_on='ID_Client',
    right_on='Client_ID',
    how='inner'
)


[OK] MANIPULATION DES DATES

# === CRÉER DES DATES ===

import pandas as pd

# Créer une date à partir d'une chaîne:
date = pd.to_datetime('2024-01-15')

# Créer une Series de dates:
dates = pd.to_datetime(['2024-01-15', '2024-02-20', '2024-03-10'])

# Format personnalisé:
dates = pd.to_datetime(['15/01/2024', '20/02/2024'], format='%d/%m/%Y')


# === CRÉER UNE PLAGE DE DATES ===

# Date range (plage de dates):
dates = pd.date_range(start='2024-01-01', end='2024-12-31', freq='D')
# freq='D' = journalier

# Fréquences disponibles:
# 'D' = jour
# 'W' = semaine
# 'M' = mois
# 'Q' = trimestre
# 'Y' = année
# 'H' = heure
# 'T' ou 'min' = minute

# Exemple: toutes les semaines en 2024
dates = pd.date_range(start='2024-01-01', end='2024-12-31', freq='W')

# Exemple: 30 dates à partir d'aujourd'hui
dates = pd.date_range(start=pd.Timestamp.now(), periods=30, freq='D')


# === EXTRAIRE LES COMPOSANTES D'UNE DATE ===

# Créer un DataFrame avec des dates:
df = pd.DataFrame({
    'Date': pd.date_range('2024-01-01', periods=10, freq='D'),
    'Ventes': [100, 150, 200, 120, 180, 220, 140, 160, 190, 210]
})

# Extraire l'année:
df['Année'] = df['Date'].dt.year

# Extraire le mois:
df['Mois'] = df['Date'].dt.month

# Extraire le jour:
df['Jour'] = df['Date'].dt.day

# Extraire le jour de la semaine (0=lundi, 6=dimanche):
df['Jour_Semaine'] = df['Date'].dt.dayofweek

# Extraire le nom du jour:
df['Nom_Jour'] = df['Date'].dt.day_name()

# Extraire le nom du mois:
df['Nom_Mois'] = df['Date'].dt.month_name()

# Extraire le trimestre:
df['Trimestre'] = df['Date'].dt.quarter

print(df)


# === CALCULER DES INTERVALLES DE TEMPS ===

# Calculer la différence entre deux dates:
date1 = pd.to_datetime('2024-01-15')
date2 = pd.to_datetime('2024-03-20')

difference = date2 - date1
print(difference)  # 65 days 00:00:00

# Extraire le nombre de jours:
nb_jours = difference.days
print(nb_jours)  # 65


# === FILTRER PAR DATE ===

# Créer un DataFrame avec des dates:
df = pd.DataFrame({
    'Date': pd.date_range('2024-01-01', periods=365, freq='D'),
    'Ventes': np.random.randint(100, 500, 365)
})

# Définir la colonne Date comme index:
df = df.set_index('Date')

# Filtrer toutes les ventes en janvier 2024:
janvier = df['2024-01']

# Filtrer une plage de dates:
q1 = df['2024-01-01':'2024-03-31']

# Filtrer avec conditions:
mars = df[df.index.month == 3]


# === RESAMPLING (AGRÉGATION TEMPORELLE) ===

# Pourquoi? Pour agréger des données à une fréquence différente

# Exemple: données journalières -> mensuelles

# Somme des ventes par mois:
ventes_mensuelles = df.resample('M').sum()

# Moyenne des ventes par semaine:
ventes_hebdo = df.resample('W').mean()

# Fréquences disponibles:
# 'D' = jour
# 'W' = semaine
# 'M' = mois
# 'Q' = trimestre
# 'Y' = année

# Avec plusieurs fonctions:
stats = df.resample('M').agg(['sum', 'mean', 'max'])


# === DÉCALAGE TEMPOREL (SHIFT) ===

# Pourquoi? Pour comparer avec la période précédente

# Décaler d'une période vers le bas:
df['Ventes_Hier'] = df['Ventes'].shift(1)

# Décaler vers le haut:
df['Ventes_Demain'] = df['Ventes'].shift(-1)

# Calculer la variation par rapport à hier:
df['Variation'] = df['Ventes'] - df['Ventes_Hier']


# === ROLLING (MOYENNE MOBILE) ===

# Pourquoi? Pour lisser les variations et voir les tendances

# Moyenne mobile sur 7 jours:
df['MA_7j'] = df['Ventes'].rolling(window=7).mean()

# Explications:
# - rolling(window=7) = fenêtre glissante de 7 jours
# - .mean() = moyenne de cette fenêtre

# Somme glissante sur 30 jours:
df['Somme_30j'] = df['Ventes'].rolling(window=30).sum()


[OK] VISUALISATION DE DONNÉES

# === BIBLIOTHÈQUES DE VISUALISATION ===

import matplotlib.pyplot as plt
import seaborn as sns

# Configurer le style:
sns.set_style('whitegrid')
plt.rcParams['figure.figsize'] = (10, 6)


# === GRAPHIQUES AVEC PANDAS ===

# Pandas intègre Matplotlib pour des graphiques rapides

# Créer des données d'exemple:
data = {
    'Mois': ['Jan', 'Fev', 'Mar', 'Avr', 'Mai'],
    'Ventes': [3000, 3500, 3200, 4000, 4500]
}
df = pd.DataFrame(data)


# === GRAPHIQUE EN LIGNES ===

df.plot(x='Mois', y='Ventes', kind='line')
plt.title('Évolution des ventes')
plt.xlabel('Mois')
plt.ylabel('Ventes (€)')
plt.show()


# === GRAPHIQUE EN BARRES ===

df.plot(x='Mois', y='Ventes', kind='bar')
plt.title('Ventes par mois')
plt.show()

# Barres horizontales:
df.plot(x='Mois', y='Ventes', kind='barh')


# === HISTOGRAMME (DISTRIBUTION) ===

# Pourquoi? Pour voir la répartition des valeurs

# Créer des données aléatoires:
ages = pd.Series(np.random.randint(18, 65, 1000))

ages.plot(kind='hist', bins=20)
plt.title('Distribution des âges')
plt.xlabel('Âge')
plt.ylabel('Fréquence')
plt.show()

# bins=20 = nombre de barres


# === BOÎTE À MOUSTACHES (BOXPLOT) ===

# Pourquoi? Pour voir la médiane, quartiles, valeurs extrêmes

df.boxplot(column='Ventes')
plt.title('Boîte à moustaches des ventes')
plt.show()


# === GRAPHIQUE EN CAMEMBERT (PIE CHART) ===

df.plot(x='Mois', y='Ventes', kind='pie', autopct='%1.1f%%')
plt.title('Répartition des ventes')
plt.ylabel('')  # Enlever le label 'Ventes'
plt.show()


# === GRAPHIQUE DE DISPERSION (SCATTER) ===

# Pourquoi? Pour voir la corrélation entre deux variables

data = {
    'Publicité': [100, 200, 150, 300, 250],
    'Ventes': [3000, 3500, 3200, 4000, 3800]
}
df = pd.DataFrame(data)

df.plot(x='Publicité', y='Ventes', kind='scatter')
plt.title('Publicité vs Ventes')
plt.show()


# === GRAPHIQUES AVANCÉS AVEC SEABORN ===

# Seaborn = Matplotlib mais plus beau et plus simple


# === GRAPHIQUE EN LIGNES (SEABORN) ===

sns.lineplot(data=df, x='Mois', y='Ventes')
plt.title('Évolution des ventes')
plt.show()


# === GRAPHIQUE EN BARRES (SEABORN) ===

sns.barplot(data=df, x='Mois', y='Ventes')
plt.title('Ventes par mois')
plt.show()


# === HEATMAP (CARTE DE CHALEUR) ===

# Pourquoi? Pour visualiser une matrice de corrélation

# Créer des données corrélées:
data = {
    'Publicité': [100, 200, 150, 300, 250],
    'Ventes': [3000, 3500, 3200, 4000, 3800],
    'Prix': [50, 55, 52, 60, 58]
}
df = pd.DataFrame(data)

# Calculer la corrélation:
corr = df.corr()

# Visualiser:
sns.heatmap(corr, annot=True, cmap='coolwarm')
plt.title('Matrice de corrélation')
plt.show()

# Explications:
# - annot=True = afficher les valeurs
# - cmap='coolwarm' = palette de couleurs


# === PAIRPLOT (MATRICE DE SCATTER) ===

# Pourquoi? Pour voir toutes les corrélations d'un coup

sns.pairplot(df)
plt.show()

# Crée un scatter plot pour chaque paire de variables


# === VIOLIN PLOT ===

# Pourquoi? Comme boxplot mais montre la distribution complète

sns.violinplot(data=df, y='Ventes')
plt.title('Distribution des ventes')
plt.show()


# === ENREGISTRER UN GRAPHIQUE ===

# Enregistrer en PNG:
plt.savefig('mon_graphique.png', dpi=300, bbox_inches='tight')

# dpi=300 = haute résolution
# bbox_inches='tight' = pas d'espace blanc autour


[OK] EXEMPLE COMPLET: ANALYSE DE VENTES

# === SCÉNARIO ===

# Tu as un fichier CSV "ventes.csv" avec:
# - Date
# - Produit
# - Catégorie
# - Prix
# - Quantité
# - Ville

# Tu veux analyser:
# 1. Quels produits se vendent le mieux?
# 2. Quelle ville génère le plus de revenus?
# 3. Y a-t-il des tendances temporelles?
# 4. Quelle catégorie est la plus rentable?


# === ÉTAPE 1: CHARGER LES DONNÉES ===

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Charger le CSV:
df = pd.read_csv('ventes.csv')

# Afficher les premières lignes:
print(df.head())


# === ÉTAPE 2: EXPLORER LES DONNÉES ===

# Informations générales:
print(df.info())

# Statistiques descriptives:
print(df.describe())

# Vérifier les valeurs manquantes:
print(df.isnull().sum())


# === ÉTAPE 3: NETTOYER LES DONNÉES ===

# Convertir la colonne Date en datetime:
df['Date'] = pd.to_datetime(df['Date'])

# Supprimer les lignes avec NaN:
df = df.dropna()

# Supprimer les doublons:
df = df.drop_duplicates()

# Vérifier les types:
print(df.dtypes)


# === ÉTAPE 4: CRÉER DE NOUVELLES COLONNES ===

# Calculer le chiffre d'affaires:
df['CA'] = df['Prix'] * df['Quantité']

# Extraire le mois:
df['Mois'] = df['Date'].dt.month

# Extraire l'année:
df['Année'] = df['Date'].dt.year

# Nom du mois:
df['Nom_Mois'] = df['Date'].dt.month_name()


# === ÉTAPE 5: ANALYSE PAR PRODUIT ===

# Top 10 produits par CA:
top_produits = df.groupby('Produit')['CA'].sum().sort_values(ascending=False).head(10)

print("Top 10 produits:")
print(top_produits)

# Visualiser:
top_produits.plot(kind='barh')
plt.title('Top 10 produits par CA')
plt.xlabel('Chiffre d\'affaires (€)')
plt.ylabel('Produit')
plt.tight_layout()
plt.show()


# === ÉTAPE 6: ANALYSE PAR VILLE ===

# CA par ville:
ca_par_ville = df.groupby('Ville')['CA'].sum().sort_values(ascending=False)

print("CA par ville:")
print(ca_par_ville)

# Visualiser:
ca_par_ville.plot(kind='pie', autopct='%1.1f%%')
plt.title('Répartition du CA par ville')
plt.ylabel('')
plt.show()


# === ÉTAPE 7: ANALYSE TEMPORELLE ===

# CA par mois:
ca_par_mois = df.groupby('Nom_Mois')['CA'].sum()

# Ordre chronologique:
ordre_mois = ['January', 'February', 'March', 'April', 'May', 'June',
              'July', 'August', 'September', 'October', 'November', 'December']
ca_par_mois = ca_par_mois.reindex(ordre_mois)

# Visualiser:
ca_par_mois.plot(kind='line', marker='o')
plt.title('Évolution du CA par mois')
plt.xlabel('Mois')
plt.ylabel('CA (€)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()


# === ÉTAPE 8: ANALYSE PAR CATÉGORIE ===

# Statistiques par catégorie:
stats_categorie = df.groupby('Catégorie').agg({
    'CA': ['sum', 'mean', 'count'],
    'Quantité': 'sum'
})

print("Statistiques par catégorie:")
print(stats_categorie)

# CA par catégorie:
ca_categorie = df.groupby('Catégorie')['CA'].sum()

# Visualiser:
ca_categorie.plot(kind='bar')
plt.title('CA par catégorie')
plt.xlabel('Catégorie')
plt.ylabel('CA (€)')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()


# === ÉTAPE 9: CORRÉLATION ===

# Voir la corrélation entre Prix et Quantité:
correlation = df[['Prix', 'Quantité', 'CA']].corr()

print("Matrice de corrélation:")
print(correlation)

# Visualiser:
sns.heatmap(correlation, annot=True, cmap='coolwarm')
plt.title('Corrélation entre variables')
plt.tight_layout()
plt.show()


# === ÉTAPE 10: EXPORTER LES RÉSULTATS ===

# Exporter en CSV:
top_produits.to_csv('top_produits.csv')

# Exporter en Excel:
with pd.ExcelWriter('analyse_ventes.xlsx') as writer:
    ca_par_ville.to_excel(writer, sheet_name='CA_Ville')
    ca_par_mois.to_excel(writer, sheet_name='CA_Mois')
    stats_categorie.to_excel(writer, sheet_name='Stats_Categorie')

print("Analyse terminée! Fichiers exportés.")


[OK] NUMPY: CALCULS NUMÉRIQUES AVANCÉS

# === INTRODUCTION À NUMPY ===

# NumPy = Numerical Python
# = Bibliothèque pour calculs numériques ultra-rapides
# = Pandas est construit sur NumPy

import numpy as np


# === CRÉER DES ARRAYS ===

# Array 1D (vecteur):
arr = np.array([1, 2, 3, 4, 5])
print(arr)  # [1 2 3 4 5]

# Array 2D (matrice):
arr_2d = np.array([[1, 2, 3], [4, 5, 6]])
print(arr_2d)
# [[1 2 3]
#  [4 5 6]]


# === CRÉER DES ARRAYS SPÉCIAUX ===

# Array de zéros:
zeros = np.zeros(5)
# [0. 0. 0. 0. 0.]

# Array de uns:
ones = np.ones((3, 3))
# [[1. 1. 1.]
#  [1. 1. 1.]
#  [1. 1. 1.]]

# Array avec une valeur spécifique:
fives = np.full((2, 4), 5)
# [[5 5 5 5]
#  [5 5 5 5]]

# Array avec plage de valeurs:
range_arr = np.arange(0, 10, 2)
# [0 2 4 6 8]

# Array avec valeurs espacées linéairement:
linspace = np.linspace(0, 1, 5)
# [0.   0.25 0.5  0.75 1.  ]


# === OPÉRATIONS MATHÉMATIQUES ===

arr = np.array([1, 2, 3, 4, 5])

# Addition:
arr + 10
# [11 12 13 14 15]

# Multiplication:
arr * 2
# [ 2  4  6  8 10]

# Puissance:
arr ** 2
# [ 1  4  9 16 25]

# Racine carrée:
np.sqrt(arr)
# [1.         1.41421356 1.73205081 2.         2.23606798]

# Exponentielle:
np.exp(arr)

# Logarithme:
np.log(arr)


# === STATISTIQUES ===

arr = np.array([10, 20, 30, 40, 50])

# Somme:
np.sum(arr)  # 150

# Moyenne:
np.mean(arr)  # 30.0

# Médiane:
np.median(arr)  # 30.0

# Écart-type:
np.std(arr)  # 14.142135623730951

# Min / Max:
np.min(arr)  # 10
np.max(arr)  # 50


# === GÉNÉRATION DE NOMBRES ALÉATOIRES ===

# Nombres aléatoires entre 0 et 1:
np.random.rand(5)
# [0.54881350.71518937 0.60276338 0.54488318 0.4236548 ]

# Nombres aléatoires entre a et b:
np.random.uniform(10, 20, size=5)
# [15.67944642 11.09445076 17.27826117 14.30835914 19.02216077]

# Entiers aléatoires:
np.random.randint(1, 100, size=10)
# [44  4 63 51 13 63 69 35 39 88]

# Distribution normale (gaussienne):
np.random.normal(loc=0, scale=1, size=1000)
# loc = moyenne, scale = écart-type


[OK] TRAITEMENT DE DONNÉES AVANCÉ

# === GÉRER LES OUTLIERS (VALEURS ABERRANTES) ===

# Pourquoi? Les outliers faussent les statistiques

# Créer des données avec outliers:
data = np.array([10, 12, 15, 14, 11, 13, 1000, 12, 11, 14])

# Méthode 1: IQR (Interquartile Range)
Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
IQR = Q3 - Q1

# Définir les limites:
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

# Filtrer:
data_clean = data[(data >= lower_bound) & (data <= upper_bound)]

# Méthode 2: Z-score
from scipy import stats

z_scores = np.abs(stats.zscore(data))
data_clean = data[z_scores < 3]


# === NORMALISATION (MISE À L'ÉCHELLE) ===

# Pourquoi? Pour comparer des variables d'échelles différentes

# Exemple: Âge (20-60) vs Salaire (2000-8000)

# Min-Max Scaling (entre 0 et 1):
from sklearn.preprocessing import MinMaxScaler

scaler = MinMaxScaler()
data_normalized = scaler.fit_transform(df[['Âge', 'Salaire']])

# Standardisation (Z-score, moyenne=0, std=1):
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
data_standardized = scaler.fit_transform(df[['Âge', 'Salaire']])


# === ENCODAGE DES VARIABLES CATÉGORIELLES ===

# Pourquoi? Les algorithmes ML ne comprennent que les nombres

# Exemple: Ville = 'Paris', 'Lyon', 'Marseille'

# Méthode 1: Label Encoding (ordinal)
from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()
df['Ville_Encoded'] = le.fit_transform(df['Ville'])

# Paris -> 0, Lyon -> 1, Marseille -> 2

# Méthode 2: One-Hot Encoding (non-ordinal)
df_encoded = pd.get_dummies(df, columns=['Ville'], prefix='Ville')

# Crée des colonnes binaires:
# Ville_Paris, Ville_Lyon, Ville_Marseille
# Chaque ligne a un 1 dans la colonne correspondante


# === CRÉATION DE BINS (CATÉGORIES) ===

# Pourquoi? Pour regrouper des valeurs continues

# Exemple: Âge -> Catégories 'Jeune', 'Adulte', 'Senior'

df['Catégorie_Âge'] = pd.cut(
    df['Âge'],
    bins=[0, 30, 50, 100],
    labels=['Jeune', 'Adulte', 'Senior']
)

# bins = limites des intervalles
# labels = noms des catégories


# === DÉTECTION D'ANOMALIES ===

# Méthode Isolation Forest:
from sklearn.ensemble import IsolationForest

model = IsolationForest(contamination=0.1)
df['Anomalie'] = model.fit_predict(df[['Prix', 'Quantité']])

# -1 = anomalie, 1 = normal


[OK] OPTIMISATION ET PERFORMANCE

# === POURQUOI OPTIMISER? ===

# - Les DataFrames peuvent être très gros (millions de lignes)
# - Des opérations lentes = attente de plusieurs minutes/heures
# - Optimiser = économiser du temps et de la mémoire


# === MESURER LE TEMPS D'EXÉCUTION ===

import time

start = time.time()

# Ton code ici
df.groupby('Ville')['CA'].sum()

end = time.time()
print(f"Temps: {end - start:.4f} secondes")


# === UTILISER DES TYPES DE DONNÉES EFFICACES ===

# int64 utilise 8 bytes, int8 utilise 1 byte!

# Avant:
df['Quantité'].dtype  # int64

# Optimiser:
df['Quantité'] = df['Quantité'].astype('int32')

# Pour des catégories:
df['Ville'] = df['Ville'].astype('category')


# === ÉVITER LES BOUCLES ===

# LENT (boucle Python):
for i in range(len(df)):
    df.loc[i, 'CA'] = df.loc[i, 'Prix'] * df.loc[i, 'Quantité']

# RAPIDE (vectorisation):
df['CA'] = df['Prix'] * df['Quantité']


# === UTILISER DASK POUR DE TRÈS GROS DATASETS ===

# Dask = Pandas parallélisé pour plusieurs CPU

import dask.dataframe as dd

# Charger avec Dask:
df_dask = dd.read_csv('ventes.csv')

# Opérations identiques à Pandas:
result = df_dask.groupby('Ville')['CA'].sum().compute()


# === LECTURE PARTIELLE DES FICHIERS ===

# Lire seulement certaines colonnes:
df = pd.read_csv('ventes.csv', usecols=['Date', 'Produit', 'CA'])

# Lire par morceaux (chunking):
for chunk in pd.read_csv('ventes.csv', chunksize=10000):
    # Traiter chunk par chunk
    print(chunk.head())


[OK] EXPORT ET SAUVEGARDE

# === EXPORTER EN CSV ===

df.to_csv('resultats.csv', index=False)

# index=False = ne pas inclure l'index


# === EXPORTER EN EXCEL ===

df.to_excel('resultats.xlsx', index=False, sheet_name='Ventes')

# Plusieurs feuilles:
with pd.ExcelWriter('resultats.xlsx') as writer:
    df1.to_excel(writer, sheet_name='Ventes')
    df2.to_excel(writer, sheet_name='Clients')


# === EXPORTER EN JSON ===

df.to_json('resultats.json', orient='records')


# === EXPORTER EN HTML ===

df.to_html('resultats.html', index=False)


# === SAUVEGARDER EN PICKLE (RAPIDE) ===

# Pickle = format binaire Python

df.to_pickle('resultats.pkl')

# Charger:
df = pd.read_pickle('resultats.pkl')


[OK] RESSOURCES ET LIENS

# Documentation officielle Pandas:
# https://pandas.pydata.org/docs/

# Tutoriels:
# https://pandas.pydata.org/docs/getting_started/tutorials.html

# NumPy:
# https://numpy.org/doc/

# Matplotlib:
# https://matplotlib.org/stable/contents.html

# Seaborn:
# https://seaborn.pydata.org/

# Kaggle (datasets gratuits):
# https://www.kaggle.com/datasets

# Cours gratuits:
# DataCamp: https://www.datacamp.com/
# Coursera: https://www.coursera.org/
# FreeCodeCamp: https://www.freecodecamp.org/

# Communauté:
# Stack Overflow: https://stackoverflow.com/questions/tagged/pandas
# Reddit: https://www.reddit.com/r/datascience/
```

Voilà le fichier complet `python_cheats/cheatsheets/analyse_de_données.txt` ! [BRAVO]

Ce guide couvre **tout ce qu'un grand débutant doit savoir** sur l'analyse de données en Python, avec des explications détaillées sur le POURQUOI, COMMENT et QUAND utiliser chaque technique.